package com.komorebi.friends.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.komorebi.friends.common.ErrorCode;
import com.komorebi.friends.constant.RedisConstant;
import com.komorebi.friends.controller.TeamUpdateRequest;
import com.komorebi.friends.exception.BusinessException;
import com.komorebi.friends.model.dto.team.TeamDeleteRequest;
import com.komorebi.friends.model.dto.team.TeamJoinRequest;
import com.komorebi.friends.model.dto.team.TeamQueryRequest;
import com.komorebi.friends.model.dto.team.TeamQuitRequest;
import com.komorebi.friends.model.entity.Team;
import com.komorebi.friends.model.entity.User;
import com.komorebi.friends.model.entity.UserTeam;
import com.komorebi.friends.model.enums.TeamStatusEnum;
import com.komorebi.friends.model.vo.TeamUserVO;
import com.komorebi.friends.model.vo.UserVO;
import com.komorebi.friends.service.TeamService;
import com.komorebi.friends.mapper.TeamMapper;
import com.komorebi.friends.service.UserService;
import com.komorebi.friends.service.UserTeamService;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author LX-MF
* @description 针对表【team】的数据库操作Service实现
* @createDate 2024-10-12 23:16:55
*/
@Service
@Slf4j
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
    implements TeamService{

    @Autowired
    private UserService userService;

    @Autowired
    private UserTeamServiceImpl userTeamService;

    @Autowired
    private RedissonClient redissonClient;

    private static final String SALT = "komorebi";

    @Transactional(rollbackFor = Exception.class)   //修改了两个表
    @Override
    public Long addTeam(Team team, User loginUser) {
        // 校验登录用户是否存在
        if(loginUser == null || loginUser.getId() == null){
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "请先登录");
        }
        // 校验队伍人数是否合法
        Integer maxNum = team.getMaxNum();
        if(maxNum == null || maxNum <= 1 || maxNum > 20){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数范围为1-20");
        }
        // 校验队伍标题
        String teamName = team.getName();
        if(StrUtil.isBlank(teamName) || teamName.length() > 20){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍标题不满足要求");
        }
        // 校验队伍描述
        String description = team.getDescription();
        if(StrUtil.isNotBlank(description) && description.length() > 512){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述不满足要求");
        }

        // 队伍开放状态，不传默认为0
        int status = Optional.ofNullable(team.getStatus()).orElse(0);
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.fromValue(status);
        if(teamStatusEnum == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态不合法");
        }
        // 密码校验
        String password = team.getPassword();
        if(TeamStatusEnum.SECRET.equals(teamStatusEnum) ){  // 私密队伍
            //密码状态
            if(StrUtil.isBlank(password) || password.length() > 32){
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码不满足要求");
            }
        }

        //  校验过期时间
        if(new Date().after(team.getExpireTime())){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍过期时间已过");
        }
        //  校验用户最多创建5个队伍
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", loginUser.getId());
        long count = this.count(queryWrapper);  // 传入的queryWrapper 声明的类得与count调用的类一致
        if(count >= 5){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户最多创建5个队伍");
        }

        // 保存队伍信息
        team.setId(null);
        team.setUserId(loginUser.getId());
        team.setStatus(teamStatusEnum.getValue());
        //  加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
        team.setPassword(encryptPassword);
        boolean result = this.save(team);
        if(!result|| team.getId() == null){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "保存队伍信息失败");
        }
        Long teamId = team.getId();

        //  保存队伍成员关系
        UserTeam userTeam = new UserTeam();
        userTeam.setTeamId(team.getId());
        userTeam.setUserId(loginUser.getId());
        userTeam.setJoinTime(new Date());
        boolean res1 = userTeamService.save(userTeam);
        if(!res1){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "保存队伍成员关系失败");
        }
        return teamId;

    }

    @Override
    public List<TeamUserVO> listTeam(TeamQueryRequest TeamQueryRequest, boolean admin) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        // 在Controller 层进行参数校验，这里不做重复校验
        //  从请求参数中取出队伍名称等查询条件，如果存在则作为查询条件
        Long id = TeamQueryRequest.getId();
        List<Long> idList = TeamQueryRequest.getIdList();
        String searchText = TeamQueryRequest.getSearchText();
        String name = TeamQueryRequest.getName();
        String description = TeamQueryRequest.getDescription();
        Integer maxNum = TeamQueryRequest.getMaxNum();
        Long userId = TeamQueryRequest.getUserId();
        Integer status = TeamQueryRequest.getStatus();
        queryWrapper.eq((id!= null && id > 0), "id", id);
        queryWrapper.in((CollectionUtil.isNotEmpty(idList)), "id", idList);
        queryWrapper.like((StrUtil.isNotBlank(name)), "name", name);
        queryWrapper.like((StrUtil.isNotBlank(description)), "description", description);
        queryWrapper.eq((maxNum!= null && maxNum > 0), "maxNum", maxNum);
        queryWrapper.eq((userId!= null && userId > 0), "userId", userId);
        //  可以通过某个关键词同时对名称和描述查询
        queryWrapper.and(StrUtil.isNotBlank(searchText),qw->{
            qw.like("name",searchText).or().like("description",searchText);
        });


        //  管理员可以看到所有状态的队伍 ，普通用户不能看到私密状态的队伍
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.fromValue(status);
        if(teamStatusEnum == null){   //如果是不存在的状态
            if(!admin) queryWrapper.ne("status", TeamStatusEnum.PRIVATE.getValue());
        }
        else{
            if(!admin && teamStatusEnum.equals(TeamStatusEnum.PRIVATE)){
                throw new BusinessException(ErrorCode.NO_AUTH_ERROR, "无权限查看私密队伍");
            }
            queryWrapper.eq("status", teamStatusEnum.getValue());
        }

        // 不展示已过期的队伍（根据过期时间筛选
        // 过期时间在现在时间之后或者没有过期时间的队伍可以展示
        queryWrapper.and(qw->{
            qw.gt("expireTime",new Date()).or().isNull("expireTime");
        });


        List<Team> teamList = this.list(queryWrapper);
        if(teamList == null || teamList.size() == 0){
            return new ArrayList<>();
        }

        // 关联查询已加入队伍的用户信息
        LinkedList<TeamUserVO> teamUserVOS = new LinkedList<>();
        for (Team team : teamList) {
            TeamUserVO teamUserVO = new TeamUserVO();
            Long teamId = team.getId();
            List<UserVO> userVOS = userTeamService.listUser(teamId);
            //
            BeanUtils.copyProperties(team,teamUserVO);
//            teamUserVO.setUserList(userVOS);
            teamUserVO.setHasJoinNum((long) userVOS.size());
            teamUserVOS.add(teamUserVO);
        }
        return teamUserVOS;
    }

    @Override
    public boolean updateTeam(TeamUpdateRequest teamUpdateRequest, User loginUser) {
        Long id = teamUpdateRequest.getId();
        String name = teamUpdateRequest.getName();
        String description = teamUpdateRequest.getDescription();
        Integer maxNum = teamUpdateRequest.getMaxNum();
        Long userId = teamUpdateRequest.getUserId();
        Integer status = teamUpdateRequest.getStatus();
        Date expireTime = teamUpdateRequest.getExpireTime();
        String password = teamUpdateRequest.getPassword();
        // 查询 id 是否合法
        if(id == null || id < 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍id不合法");
        }
        // 查询队伍是否存在
        Team preTeam = this.getById(id);
        if(preTeam == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍不存在");
        }

        // 只有管理员或者队伍的创建者可以修改
        if(!userService.isAdmin(loginUser) && !(loginUser.getId().equals(preTeam.getUserId()))){
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR,"没有权限修改队伍");
        }
        // 如果队伍状态改为加密，必须要有密码
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.fromValue(status);
        if(teamStatusEnum != null && teamStatusEnum.equals(TeamStatusEnum.SECRET) && StrUtil.isBlank(password)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"加密状态填写密码");
        }
        // 如果用户传入的新值和旧值一致，就不用 update 了（可自行实现，降低数据库使用次数）
        //获取旧值
        UpdateWrapper<Team> teamUpdateWrapper = new UpdateWrapper<>();
        teamUpdateWrapper.eq("id",id);
        teamUpdateWrapper.set(StrUtil.isNotBlank(name) && !name.equals(preTeam.getName()),"name",name);
        teamUpdateWrapper.set(StrUtil.isNotBlank(description) && !description.equals(preTeam.getDescription()),"description",description);
        teamUpdateWrapper.set(maxNum != null && !maxNum.equals(preTeam.getMaxNum()),"maxNum",maxNum);
        teamUpdateWrapper.set(userId != null && !userId.equals(preTeam.getUserId()),"userId",userId);
        teamUpdateWrapper.set(status != null && !status.equals(preTeam.getStatus()),"status",status);
        teamUpdateWrapper.set(expireTime != null && !expireTime.equals(preTeam.getExpireTime()),"expireTime",expireTime);
        teamUpdateWrapper.set(StrUtil.isNotBlank(password) && !password.equals(preTeam.getPassword()),"password",password);
        boolean update = false;
        try {
            update = this.update(null, teamUpdateWrapper);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return update;
    }

    @Override
    public boolean joinTeam(TeamJoinRequest teamJoinRequest, User loginUser) {
        Long teamId = teamJoinRequest.getTeamId();
        String password = teamJoinRequest.getPassword();
        if(teamId == null || teamId < 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"id格式错误");
        }
        Team team = this.getById(teamId);
        if(team == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"无法找到对应队伍");
        }
        Long userId = loginUser.getId();

        //如果出现多个请求来执行同样的加入队伍的请求，要竞争锁，抢到的可以向下进行
        String key = RedisConstant.REDIS_JOIN_TEAM_LOCK_PREFIX + teamId;
        RLock lock = redissonClient.getLock(key);

        try {
            while(true){
                //抢锁
                if(lock.tryLock(0,-1, TimeUnit.MILLISECONDS)){
                    //  用户最多加入 5 个队伍
                    int count = userTeamService.countTeam(userId);
                    if(count >= 5) {
                        throw new BusinessException(ErrorCode.OPERATION_ERROR,"用户已达到加入队伍上限");
                    }
                    //  队伍必须存在，只能加入未满的
                    int countUser = userTeamService.countUser(teamId);
                    if(countUser >= team.getMaxNum()){
                        throw new BusinessException(ErrorCode.OPERATION_ERROR,"队伍已满");
                    }
                    // 队伍不能过期
                    Date expireTime = team.getExpireTime();
                    if(expireTime != null && new Date().after(expireTime)){
                        throw new BusinessException(ErrorCode.OPERATION_ERROR,"队伍已过期");
                    }
                    //  不能加入自己的队伍，不能重复加入已加入的队伍（幂等性）
                    if(userId.equals(team.getUserId())){
                        throw new BusinessException(ErrorCode.OPERATION_ERROR,"已在队伍中");
                    }
                    QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
                    userTeamQueryWrapper.eq("teamId",teamId).eq("userId",userId);
                    UserTeam record = userTeamService.getOne(userTeamQueryWrapper);
                    if(record != null){
                        throw new BusinessException(ErrorCode.OPERATION_ERROR,"已在队伍中");
                    }
                    //  禁止加入私有的队伍
                    TeamStatusEnum teamStatusEnum = TeamStatusEnum.fromValue(team.getStatus());
                    if(teamStatusEnum.equals(TeamStatusEnum.PRIVATE)){
                        throw new BusinessException(ErrorCode.NO_AUTH_ERROR,"不可加入私有队伍");
                    }
                    //  如果加入的队伍是加密的，必须密码匹配才可以
                    // 2. 加密
                    if(teamStatusEnum.equals(TeamStatusEnum.SECRET) && StrUtil.isBlank(password)){
                        throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码不能为空");
                    }
                    String encryptPassword = DigestUtils.md5DigestAsHex((SALT + password).getBytes());
                    if(teamStatusEnum.equals(TeamStatusEnum.SECRET)){
                        if(!encryptPassword.equals(team.getPassword())){
                            throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码不匹配");
                        }
                    }
                    //  新增队伍 - 用户关联信
                    UserTeam userTeam = new UserTeam();
                    userTeam.setTeamId(teamId);
                    userTeam.setUserId(userId);
                    userTeam.setJoinTime(new Date());
                    return userTeamService.save(userTeam);
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
            log.error("获取锁异常");
            return false;
        } finally {
            if(lock.isHeldByCurrentThread()){
                log.info("加入队伍锁释放");
                lock.unlock();
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)   //修改了两个表
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, User loginUser) {
        Long teamId = teamQuitRequest.getId();
        Long userId = loginUser.getId();
        if (teamId == null || teamId < 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "id不正确");
        }
        Team team = this.getById(teamId);
        if (team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍不存在");
        }
        //用户是否在队伍中
        UserTeam userTeam = userTeamService.find(teamId, userId);
        if (userTeam == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "用户不在队伍中");
        }
        //队伍里的人数
        int countUser = userTeamService.countUser(teamId);
        if (countUser == 1) {
            // 队伍只剩下最后一个人，解散队伍(删除队伍信息表)
            boolean removeById = this.removeById(teamId);
            if (!removeById) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除队伍信息错误");
            }
        } else {
            //如果是队长
            if (loginUser.getId().equals(team.getUserId())) {
                // 权限转移给第二早加入队伍的人，按时间排序user_team表，修改userId
                int current = 2;
                UserTeam next = userTeamService.findByJoinTime(teamId, current);
                // 修改userId
                Team newTeam = new Team();
                newTeam.setId(teamId);
                newTeam.setUserId(next.getUserId());
                boolean updateById = this.updateById(newTeam);
                if (!updateById) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "修改队伍所属错误");
                }
            }
        }
        //删除user_team表里的记录
        boolean res = userTeamService.removeById(userTeam.getId());
        if (!res) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除用户_队伍信息表错误");
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)   //修改了两个表
    public boolean deleteTeam(TeamDeleteRequest teamDeleteRequest, User loginUser) {
        Long id = teamDeleteRequest.getId();
        Long userId = loginUser.getId();
        if(id == null || id < 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"id格式错误");
        }
        Team team = this.getById(id);
        if(team == null ){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍不存在");
        }
        if(!team.getUserId().equals(userId)){
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR,"用户没有权限解散队伍");
        }
        //移除关联信息
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("teamId",id);
        boolean b = userTeamService.remove(userTeamQueryWrapper);
        if(!b){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"删除用户队伍关联表失败");
        }
        boolean removeById = this.removeById(id);
        if(!removeById){
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"删除队伍信息失败");
        }
        return true;
    }


}




